package com.aiapphub.BaseCode.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.aiapphub.BaseCode.entity.SsoUser;
import com.aiapphub.BaseCode.mapper.SsoUserMapper;
import com.aiapphub.BaseCode.service.SSOLoginService;
import com.aiapphub.BaseCode.utils.JwtUtil;
import com.aiapphub.BaseCode.utils.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Map;

/**
 * SSO服务实现类，用于生成SSO登录URL。
 */
@Service
public class SSOLoginServiceImpl implements SSOLoginService {

    // SSO基础URL，从配置文件中读取
    @Value("${sso.base.url}")
    private String ssoBaseUrl;

    // 授权API路径，从配置文件中读取
    @Value("${sso.oauth.authorizeAPI}")
    private String authorizeApi;

    // 客户端ID，从配置文件中读取
    @Value("${sso.client.id}")
    private String clientId;

    // 响应类型，从配置文件中读取
    @Value("${sso.client.response_type}")
    private String responseType;

    // 重定向URI，从配置文件中读取
    @Value("${sso.client.redirect_uri}")
    private String redirectUri;

    // 获取令牌的API路径
    @Value("${sso.oauth.accessTokenAPI}")
    private String tokenApi;

    // 客户端密钥
    @Value("${sso.client.secret}")
    private String clientSecret;

    // Web应用的URI
    @Value("${sso.client.web_uri}")
    private String webUri;

    //用户信息API
    @Value("${sso.oauth.userInfoAPI}")
    private String userInfoApi;

    // Redis缓存
    private final RedisCache redisCache;

    @Autowired
    public SSOLoginServiceImpl(RedisCache redisCache) {
        this.redisCache = redisCache;
    }

    // 用户数据访问接口
    @Autowired
    private SsoUserMapper ssoUserMapper;

    // RestTemplate用于HTTP请求
    @Autowired
    private RestTemplate restTemplate;

    /**
     * 构建SSO登录URL。
     *
     * @return 生成的SSO登录URL。
     */
    @Override
    public String buildLoginUrl() {

        return UriComponentsBuilder.fromHttpUrl(ssoBaseUrl + authorizeApi)
                .queryParam("response_type", responseType)
                .queryParam("client_id", clientId)
                .queryParam("redirect_uri", redirectUri)
                .toUriString();
    }

    /**
     * 处理从SSO系统回调的请求，包括获取访问令牌、用户信息和生成JWT令牌。
     *
     * @param code 从SSO系统返回的授权码
     * @param session 当前会话，用于存储访问令牌
     * @param response HTTP响应，用于重定向用户或返回错误信息
     * @throws IOException 如果在处理请求过程中发生输入输出异常
     */
    @Override
    public void handleSSOCallback(String code, HttpSession session, HttpServletResponse response) throws IOException {
        try {
            // 构造获取访问令牌的URL
            String tokenUrl = ssoBaseUrl + tokenApi;
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); // 设置内容类型为表单编码

            // 构造请求体，包含授权码及其他必需参数
            MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
            body.add("client_id", clientId);
            body.add("client_secret", clientSecret);
            body.add("grant_type", "authorization_code"); // 指明授权类型
            body.add("redirect_uri", URLEncoder.encode(redirectUri, "UTF-8")); // URL编码回调URI
            body.add("code", code); // 授权码

            HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(body, headers);

            // 发送POST请求以获取访问令牌
            ResponseEntity<Map> responseEntity = restTemplate.exchange(tokenUrl, HttpMethod.POST, request, Map.class);

            if (responseEntity.getStatusCode() == HttpStatus.OK) {
                Map<String, Object> responseMap = responseEntity.getBody();
                if (responseMap != null && responseMap.containsKey("access_token")) {
                    String accessToken = (String) responseMap.get("access_token");
                    session.setAttribute("access_token", accessToken); // 存储访问令牌

                    // 使用访问令牌获取用户信息
                    ResponseEntity<Map> userInfoResponse = getUserInfo(accessToken);
                    if (userInfoResponse.getStatusCode() == HttpStatus.OK) {
                        Map<String, Object> userInfo = userInfoResponse.getBody();
                        String userId = (String) userInfo.get("uid");
                        // 创建JWT令牌并缓存
                        String token = JwtUtil.createJWT(userId);
                        redisCache.setCacheObject("loginToken:" + userId, token);

                        // 从数据库获取用户信息并缓存
                        LambdaQueryWrapper<SsoUser> queryWrapper = new LambdaQueryWrapper<>();
                        queryWrapper.eq(SsoUser::getLoginName, userId);
                        SsoUser ssoUser = ssoUserMapper.selectOne(queryWrapper);
                        String userJson = JSON.toJSONString(ssoUser);
                        redisCache.setCacheObject("userInfo:" + userId, userJson);

                        // 重定向到Web应用并附带JWT令牌
                        response.sendRedirect(webUri + "?token=" + token);
                        return;
                    }
                }
            }
            // 获取令牌失败，返回401错误
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "获取访问令牌失败");
        } catch (Exception e) {
            // 记录异常以帮助调试
            e.printStackTrace();
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "处理回调请求时出错");
        }
    }


    /**
     * 根据访问令牌获取用户信息
     *
     * @param accessToken 访问令牌
     * @return 包含用户信息的ResponseEntity
     */
    private ResponseEntity<Map> getUserInfo(String accessToken) {
        // 创建并设置请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        HttpEntity<String> requestEntity = new HttpEntity<>(headers);

        // 构建获取用户信息的URL
        String userInfoUrl = String.format("%s%s?access_token=%s", ssoBaseUrl, userInfoApi, accessToken);

        try {
            // 发送请求并返回响应
            return restTemplate.exchange(userInfoUrl, HttpMethod.POST, requestEntity, Map.class);
        } catch (Exception e) {
            // 捕获并记录异常
            e.printStackTrace();
            // 返回内部服务器错误状态
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    /**
     * 处理退出注销操作
     *
     * @param request HTTP 请求对象
     * @param response HTTP 响应对象
     * @param redirectUrl 重定向的 URL
     * @throws IOException 可能抛出的 IO 异常
     */
    @Override
    public void handleLogout(HttpServletRequest request, HttpServletResponse response, String redirectUrl) throws IOException {
        // 获取当前用户认证信息
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null) {
            // 清除认证信息
            SecurityContextHolder.clearContext();
            // 使当前会话无效
            request.getSession().invalidate();
        }

        // 根据 URL 验证结果进行重定向
        if (redirectUrl != null && isValidRedirectUrl(redirectUrl)) {
            response.sendRedirect(redirectUrl);
        } else {
            response.sendRedirect("/login?logout");
        }
    }

    /**
     * 验证重定向 URL 的有效性
     *
     * @param url 需要验证的 URL
     * @return 如果 URL 合法则返回 true，否则返回 false
     */
    private boolean isValidRedirectUrl(String url) {
        // 示例：只允许特定的 URL 前缀
        return url.startsWith("http://127.0.0.1:8080/") || url.startsWith("https://trusted-domain.com/");
    }
}